---
title: 高级特性
sidebar:
  order: 19
---

import { LinkCard } from '@astrojs/starlight/components';

Mybatis-Mate 是为 MyBatis-Plus 提供的企业级模块，旨在更敏捷优雅处理数据。

- 使用示例 ：[传送门](https://gitee.com/baomidou/mybatis-mate-examples)
- 联系作者确认后微信公众号发文介绍 Mybatis-Mate 的软文，可免费获得永久个人授权证书。
- 该模块属于 MyBatis-Plus 的扩展库，非 MyBatis-Plus 的收费版本，任何问题由 `青苗` 个人负责。

<LinkCard
  title="免费视频教程"
  description="青苗出品，质量保障"
  href="https://space.bilibili.com/212983666/channel/collectiondetail?sid=606288"
/>

## 数据审计（对账）

- 使用示例：👉 [mybatis-mate-audit](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-audit)
- 对比两对象属性差异，例如：银行流水对账。
  ```java
  // 1，异步回调，注意 @EnableAsync 开启异步
  applicationEventPublisher.publishEvent(new DataAuditEvent((t) -> {
      List<Change> changes = t.apply(newVersion, oldVersion);
      for (Change valueChange : changes) {
          ValueChange change = (ValueChange) valueChange;
          System.err.println(String.format("%s不匹配，期望值 %s 实际值 %s", change.getPropertyName(), change.getLeft(), change.getRight()));
      }
  }));

  // 2，手动调用对比
  DataAuditor.compare(obj1, obj2);
  ```

## 数据敏感词过滤

- 使用示例👉 [mybatis-mate-sensitive-words](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-sensitive-words)
- 数据敏感词过滤（AC 算法）配置完处理器，框架自动处理请求的所有字符串敏感词过滤，支持嵌套关键词让敏感词无处遁形。
- 数据库自维护敏感词库（免费、可控），默认加载缓存词根支持指定重新加载词库。

## 数据范围（数据权限）

- 使用示例：[mybatis-mate-datascope](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-datascope)
- 注解 @DataScope
  |  属性  |     类型     | 必须指定 | 默认值 | 描述                                   |
  | :----: | :----------: | :------: | :----: | -------------------------------------- |
  |  type  |    String    |    否    |   ""   | 范围类型，用于区分对于业务分类，默认空 |
  | value  | DataColumn[] |    否    |   {}   | 数据权限字段，支持多字段组合           |
  | ignore |   boolean    |    否    | false  | 忽略权限处理逻辑 true 是 false 否      |
- 注解 @DataColumn
  | 属性  |  类型  | 必须指定 | 默认值 | 描述       |
  | :---: | :----: | :------: | :----: | ---------- |
  | alias | String |    否    |   ""   | 关联表别名 |
  | name  | String |    是    |        | 字段名     |
- 行级粒度权限控制，例如：上级部门可以查看子部门信息。
  ```java
  // 测试 test 类型数据权限范围，混合分页模式
  @DataScope(type = "test", value = {
          // 关联表 user 别名 u 指定部门字段权限
          @DataColumn(alias = "u", name = "department_id"),
          // 关联表 user 别名 u 指定手机号字段（自己判断处理）
          @DataColumn(alias = "u", name = "mobile")
  })
  @Select("select u.* from user u")
  List<User> selectTestList(IPage<User> page, Long id, @Param("name") String username);

  // 测试数据权限，最终执行 SQL 语句
  SELECT u.* FROM user u WHERE (u.department_id IN ('1', '2', '3', '5')) AND u.mobile LIKE '%1533%' LIMIT 1,10
  ```

:::note[关于`IDataScopeProvider`的说明]

请注意必须注入 IDataScopeProvider 实现类处理数据权限，关于数据传参支持 2 种方式：
1，自定义 mapper 方法通过方法参数传递，在 setWhere 方法 `Object[] args` 参数中获取
2，利用 ThreadLocal 传递参数，你可以拦截 controller 层或者 service 层设置数据权限处理参数，更多可以 👉[参考](https://gitee.com/baomidou/mybatis-plus-samples/tree/master/mybatis-plus-sample-dynamic-tablename)

:::

## 表结构自动维护

- 使用示例：
  - 👉 [mybatis-mate-ddl-mysql](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-ddl-mysql)
  - 👉 [mybatis-mate-ddl-postgres](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-ddl-postgres)
- 数据库 `Schema` 初始化，升级 `SQL` 自动维护，区别于 `flyway` 支持分表库、可控制代码执行 SQL 脚本
- 首次会在数据库中生成 `ddl_history` 表，每次执行SQL脚本会自动维护版本信息。
  ```java
  @Component
  public class MysqlDdl implements IDdl {

      /**
       * 执行 SQL 脚本方式
       */
      @Override
      public List<String> getSqlFiles() {
          return Arrays.asList(
                  "db/tag-schema.sql",
                  "D:\\db\\tag-data.sql"
          );
      }
  }

  // 切换到 mysql 从库，执行 SQL 脚本
  ShardingKey.change("mysqlt2");
  ddlScript.run(new StringReader("DELETE FROM user;\n" +
          "INSERT INTO user (id, username, password, sex, email) VALUES\n" +
          "(20, 'Duo', '123456', 0, 'Duo@baomidou.com');"));
  ```

## 字段数据绑定（字典回写）

- 使用示例：👉 [mybatis-mate-dict](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-dict)
- 注解 @FieldBind
  |   属性   |  类型  | 必须指定 | 默认值 | 描述                                               |
  | :------: | :----: | :------: | :----: | -------------------------------------------------- |
  | sharding | String |    否    |   ""   | 分库分表数据源指定                                 |
  |   type   | String |    是    |        | 类型（用于区分不同业务）                           |
  |  target  | String |    是    |        | 目标显示属性（待绑定属性，注意非数据库字段请排除） |
- 数据库 `sex` 值 `0`、`1` 自动映射为 `男`、`女`
- 可以绑定映射为对象，例如：根据订单 ID 映射 订单对象或者编号
  ```java
  @FieldBind(type = "user_sex", target = "sexText")
  private Integer sex;
  // 绑定显示属性，非表字典（排除）
  @TableField(exist = false)
  private String sexText;
  ```
- 绑定业务处理类需要实现 IDataBind 接口，注入 spring 容器
  ```java
  @Component
  public class DataBind implements IDataBind {
    ...
  }
  ```

## 虚拟属性绑定

- 使用示例：👉 [mybatis-mate-jsonbind](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-jsonbind)
- 注解 @JsonBind
  ```java
  @JsonBind("绑定类型")
  public class User {
      ...
  }
  ```
- 返回 Json 虚拟属性绑定策略
  ```java
  @Component
  public class JsonBindStrategy implements IJsonBindStrategy {

      @Override
      public Map<String, Function<Object, Map<String, Object>>> getStrategyFunctionMap() {
          return new HashMap<String, Function<Object, Map<String, Object>>>(16) {
              {
                  // 注入虚拟节点
                  put(Type.departmentRole, (obj) -> new HashMap(2) {{
                      User user = (User) obj;
                      // 枚举类型转换
                      put("statusText", StatusEnum.get(user.getStatus()).getDesc());
                      // 可调用数据库查询角色信息
                      put("roleName", "经理");
                  }});
              }
          };
      }
  }
  ```

## 字段加密解密

- 使用示例：👉 [mybatis-mate-encrypt](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-encrypt)
- 注解 @FieldEncrypt
  |   属性    |   类型    | 必须指定 |      默认值      | 描述                 |
  | :-------: | :-------: | :------: | :--------------: | -------------------- |
  | password  |  String   |    否    |        ""        | 加密密码             |
  | algorithm | Algorithm |    否    | PBEWithMD5AndDES | PBE MD5 DES 混合算法 |
  | encryptor |   Class   |    否    |    IEncryptor    | 加密处理器           |
- 算法 Algorithm
  |            算法             |              描述              |
  | :-------------------------: |:----------------------------:|
  |           MD5_32            |     32 位 md5 算法              |
  |           MD5_16            |         16 位 md5 算法          |
  |           BASE64            |      64 个字符来表示任意二进制数据算法      |
  |             AES             |  AES 对称算法 【需要模糊查询的请务必使用该算法】  |
  |             RSA             |           非对称加密算法            |
  |             SM2             |    国密 SM2 非对称加密算法，基于 ECC     |
  |             SM3             | 国密 SM3 消息摘要算法，可以用 MD5 作为对比理解 |
  |             SM4             | 国密 SM4 对称加密算法，无线局域网标准的分组数据算法 |
  |      PBEWithMD5AndDES       |             混合算法             |
  |   PBEWithMD5AndTripleDES    |             混合算法             |
  | PBEWithHMACSHA512AndAES_256 |             混合算法             |
  |    PBEWithSHA1AndDESede     |             混合算法             |
  |    PBEWithSHA1AndRC2_40     |             混合算法             |
- 👉 [国密 SM2.3.4 算法使用规范](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/%E5%9B%BD%E5%AF%86SM2.3.4%E7%AE%97%E6%B3%95%E4%BD%BF%E7%94%A8%E8%A7%84%E8%8C%83)
  :::note

  - MD5 算法为不可逆算法，存储数据库及查询结果都是密文。
  - SM4 算法必须依赖 bouncycastle 加密库。
  - 混合算法必须依赖 jasypt 加密库。
  - 【注意】查询返回加密对象必须包含`加密注解`信息，单纯的返回某个 String 或者 List 某个集合是无法解密的。

  :::
- 注解 `FieldEncrypt` 实现数据加解密，支持多种加密算法
  ```java
  @FieldEncrypt
  private String email;
  ```

## 字段脱敏

- 使用示例：👉 [mybatis-mate-sensitive-jackson](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-sensitive-jackson)
- 注解 @FieldSensitive
- 注解 `FieldSensitive` 实现数据脱敏，内置 `手机号`、`邮箱`、`银行卡号` 等 9 种常用脱敏规则
  ```java
  @FieldSensitive("testStrategy")
  private String username;

  @Configuration
  public class SensitiveStrategyConfig {

      /**
       * 注入脱敏策略
       */
      @Bean
      public ISensitiveStrategy sensitiveStrategy() {
          // 自定义 testStrategy 类型脱敏处理
          return new SensitiveStrategy().addStrategy("testStrategy", t -> t + "***test***");
      }
  }

  // 跳过脱密处理，用于编辑场景
  RequestDataTransfer.skipSensitive();
  ```

## 多数据源分库分表（读写分离）

- 使用示例：👉 [mybatis-mate-sharding](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-sharding)
- 注解 @Sharding
  |   属性   |  类型  | 必须指定 |         默认值         | 描述                         |
  | :------: | :----: | :------: | :--------------------: | ---------------------------- |
  |  value   | String |    是    |           ""           | 分库组名，空使用默认主数据源 |
  | strategy | Class  |    否    | RandomShardingStrategy | 分库&分表策略                |
- 配置
  ```xml
  mybatis-mate:
    sharding:
      health: true # 健康检测
      primary: mysql # 默认选择数据源
      datasource:
        mysql: # 数据库组
          - key: node1
            ...
          - key: node2
            cluster: slave # 从库读写分离时候负责 sql 查询操作，主库 master 默认可以不写
            ...
        postgres:
          - key: node1 # 数据节点
            ...
  ```
- 注解 `Sharding` 切换数据源，组内节点默认随机选择（查从写主）
  ```java
  @Mapper
  @Sharding("mysql")
  public interface UserMapper extends BaseMapper<User> {

      @Sharding("postgres")
      Long selectByUsername(String username);

  }
  ```
- 切换指定数据库节点
  ```java
  // 切换到 mysql 从库 node2 节点
  ShardingKey.change("mysqlnode2");
  ```

## 多数据源动态加载卸载

- 使用示例：👉 [mybatis-mate-sharding-dynamic](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-sharding-dynamic)

## 多数据源事务（JTA Atomikos）

- 使用示例：👉 [mybatis-mate-sharding-jta-atomikos](https://gitee.com/baomidou/mybatis-mate-examples/tree/master/mybatis-mate-sharding-jta-atomikos)
